package demo.java.util.stream;

import org.junit.Test;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.stream.Stream;

/**
 * 其他汇聚操作：
 * <p>
 * reduce方法：reduce方法非常的通用，后面介绍的count，sum等都可以使用其实现。reduce 这个方法的主要作用是把 Stream
 * 元素组合起来。 它提供一个起始值（种子），然后依照运算规则（BinaryOperator），和前面 Stream 的第一个、第二个、第 n 个元素组合。
 * <p>
 * 从这个意义上说，字符串拼接、数值的 sum、min、max、average 都是特殊的 reduce。
 * <p>
 * 搜索相关
 * <li>allMatch：是不是Stream中的所有元素都满足给定的匹配条件
 * <li>anyMatch：Stream中是否存在任何一个元素满足匹配条件
 * <li>findFirst: 返回Stream中的第一个元素，如果Stream为空，返回空Optional
 * <li>noneMatch：是不是Stream中的所有元素都不满足给定的匹配条件
 * <li>max和min：使用给定的比较器（Operator），返回Stream中的最大|最小值
 */
public class ReduceDemo {


    @Test
    public void reduceDemo() {
        List<Integer> ints = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        // 搜索相关
        System.out.println(ints.stream().allMatch(item -> item < 100));
        ints.stream().max((o1, o2) -> o1.compareTo(o2)).ifPresent(System.out::println);

        // 字符串连接，concat = "ABCD"
        String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat);
        // 求最小值，minValue = -3.0
        double minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min);
        // 求和，sumValue = 10, 有起始值
        int sumValue = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum);
        // 求和，sumValue = 10, 无起始值
        sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();
        // 过滤，字符串连接，concat = "ace"
        concat = Stream.of("a", "B", "c", "D", "e", "F").filter(x -> x.compareTo("Z") > 0).reduce("", String::concat);
    }

    @Test
    public void reduceDemo3() {
        Optional<StringBuilder> str = Stream.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
                .map(i -> new StringBuilder(Integer.toString(i)))
                .reduce(new BinaryOperator<StringBuilder>() {
                    @Override
                    public StringBuilder apply(StringBuilder s1, StringBuilder s2) {
                        s1.append(", ").append(s2);
                        return s1;
                    }
                });
        System.out.println(str.get());
        StringBuilder sb = Stream.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
                .map(i -> new StringBuilder(Integer.toString(i)))
                .reduce(new StringBuilder(), new BinaryOperator<StringBuilder>() {
                    @Override
                    public StringBuilder apply(StringBuilder s1, StringBuilder s2) {
                        return s1.append(", ").append(s2);
                    }
                });
        System.out.println(sb);
        StringBuilder sb2 = Stream.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
                .map(i -> new StringBuilder(Integer.toString(i)))
                .reduce(new StringBuilder(), new BiFunction<StringBuilder, StringBuilder, StringBuilder>() {
                    @Override
                    public StringBuilder apply(StringBuilder s1, StringBuilder s2) {
                        return s1.append(", ").append(s2);
                    }
                }, new BinaryOperator<StringBuilder>() {
                    @Override
                    public StringBuilder apply(StringBuilder s1, StringBuilder s2) {
                        return s1.append(", ").append(s2);
                    }
                });
        System.out.println(sb2);
    }

    /**
     * reduce方法的第一种形式，其方法定义如下：Optional<T> reduce(BinaryOperator<T> accumulator);
     * <p>
     * reduce方法接受一个函数，这个函数有两个参数:
     * <li>第一个参数是上次函数执行的返回值（也称为中间结果），
     * <li>第二个参数是stream中的元素，这个函数把这两个值相加，得到的和会被赋值给下次执行这个函数的第一个参数。
     */
    @Test
    public void reduceDemo1() {
        List<Integer> ints = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        Optional<Integer> tmp = ints.stream().reduce((sum, item) -> sum + item);
        System.out.println("ints sum is:" + tmp.get());
        tmp = ints.stream().reduce(new BinaryOperator<Integer>() {
            @Override
            public Integer apply(Integer t, Integer u) {
                if (t == null && u == null) {
                    return null;
                }
                if (t != null && u == null) {
                    return t;
                }
                if (t == null && u != null) {
                    return u;
                }
                return t + u;
            }
        });
        System.out.println("ints sum is:" + tmp.get());
    }

    /**
     * T reduce(T identity, BinaryOperator<T> accumulator);
     * <p>
     * 这个定义上上面已经介绍过的基本一致，不同的是：它允许用户提供一个循环计算的初始值，如果Stream为空，就直接返回该值。
     * 而且这个方法不会返回Optional，因为其不会出现null值。
     */
    @Test
    public void reduceDemo2() {
        List<Integer> ints = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        int result = ints.stream().reduce(0, (sum, item) -> sum + item);
        System.out.println("ints sum is:" + result);

        result = ints.stream().reduce(0, new BinaryOperator<Integer>() {
            @Override
            public Integer apply(Integer t, Integer u) {
                if (t == null && u == null) {
                    return null;
                }
                if (t != null && u == null) {
                    return t;
                }
                if (t == null && u != null) {
                    return u;
                }
                return t + u;
            }
        });
        System.out.println("ints sum is:" + result);
    }
}
